<?php

/**
 *	DoryJSON.class.php
 *
 *	Developer class, where all usable functions reside. Developer only needs to import this class and call the corresponding functions. Sample implementation is showed below. 
 *	@author Ashwanth Kumar <chronosaeon@gmail.com>
 *	@version Alpha
 */	

require_once("Dory.php");
require_once("Dory.Json.php");
require_once("Dory.Settings.php");

class DoryJSON {
	
	/**
	 *	init() - Initializes the system with the default settings.
	 */	
	private function _init() {
		// TODO: Add the default init() setup
	}
	
	/**
	 *	retSuccess($data) - Returns a successful JSON data with the status as 'success'
	 */
	function retSuccess($data,$forceObject=false) {
		$result = array('status' => true,'data' => $data);
		if($forceObject) echo json_encode($result, JSON_FORCE_OBJECT);
		else echo json_encode($result);
	}
	
	/**
	 *	retError($reason) - Returns a successful error JSON data, with the status as 'error' and data as '$reason'
	 */
	function retError($reason,$forceObject=false,$die=false) {
		$result = array('status' => false,'data' => $reason);
		if($die) {
			die(json_encode($result));
		} else {
			if($forceObject) echo json_encode($result, JSON_FORCE_OBJECT);
			else echo json_encode($result);
		}
	}
	
	/**
	 *	authenticateUser - Used to authenticate the user
	 */
	function authenticateUser($username,$password,$return=false) {
		$user = new Users();
		$password = md5($password);
		$users = $user -> Find('Users',array("Username"=>$username,"Password"=>$password),array());
		
		if(sizeof($users) < 1) $this -> retError('Invalid Login Details');
		else  {
			$user = new UsersJSON($users[0]);
			$_SESSION['uid'] = $users[0] -> ID;
			$auth = new AuthSession();
			$auths = $auth -> Find('AuthSession',array("User"=>$users[0]->ID),array());
			if(sizeof($auths) < 1) { // User is not in Authenticated Sessions - Add them in
				$auth -> User = $users[0] -> ID;
				$auth -> Time = date('Y-m-d G:i:s ');
				echo $auth -> Time;
				$auth -> IP = $_SERVER['REMOTE_ADDR'];
				$auth -> Save();
				$user -> AuthSession = $auth -> ID;
			} else {
				$auth = $auths[0];
				$auth -> Time = null;
				echo $auth -> Time;
				$auth -> IP = $_SERVER['REMOTE_ADDR'];
				$auth -> Save();
				
				$user -> AuthSession = $auth -> ID;
			}
			if(!$return)	$this -> retSuccess($user);
			else return $user;
		}
	}
	
	/**
	 *	userLogout() - Logout out the user, remove the user session
	 */
	function userLogout() {
		if(isset($_SESSION['uid'])) {
			$uid = $_SESSION['uid'];
			unset($_SESSION['uid']);
			$auth = new AuthSession();
			$auths = $auth -> Find('AuthSession',array("User"=>$uid),array());
			if(sizeof($auths) > 0) {
				$auth = $auths[0];
				$auth -> DeleteYourself();
			} else {
				// Hopefully this never happens
				$this -> retError('There seems to be a problem with the user authenticated. But the user has been logged out!');
			}
		} else {
			$this -> retError('Not User is Logged In!');
		}
	}
	
	/**
	 *	getGravatarURL() - Gets the currently authenticated Users' Gravatar URL
	 */
	function getUserGravatarURL() {
		if(isset($_SESSION['uid'])) {
			$user = new Users();
			$us = $user -> Find('Users',array('ID'=>$_SESSION['uid']),array());
			$g = array("Url" => "http://www.gravatar.com/avatar/".$user -> Email."?d=identicon&s=120");
			$this -> retSuccess($g);
		} else {
			$this -> retError('User is not logged In');
		}
	}
	
	/**
	 *	getGravatarURL($email) - Gets any persons' gravatar URL based on his $email 
	 */
	function getGravatarURL($email) {
		$g = array("Url" => "http://www.gravatar.com/avatar/".$email."?d=identicon&s=120");
		$this -> retSuccess($g);
	}
	
	/**
	 *	getUserPosts() - Gets all the posts, of the current logged in user
	 */
	function getUserPosts($getComments=true) {
		if(isset($_SESSION['uid'])) {
			$post = new Posts();
			$posts = $post -> Find('Posts',array('Author'=>$_SESSION['uid']),array());
			$ps = array();	// Posts Array
			foreach($posts as $p) {
				// Adding the Counter part to the Post. Each time the posts are requested! It means, they get to view it! Increase it! I know, this isn't the best way to do it, but lets've it this way for some being. Any rectifications, gladly welcome.
				$p -> ViewCount = $p -> ViewCount++;
				$p -> Save();
				// Saving the post after updating the values!
				
				$cmts = $this -> getPostComments($p->ID,true);
				// TODO: Fetching the comment data for each post can be varied by $getComments parameter. Add the code here. By default all the fetches do've them. Marked for Beta Release!
				$p2 = new PostsJSON($p,$cmts);
				$ps[] = $p2;
			}
			$this -> retSuccess(array('count' => sizeof($ps),'posts' => $ps));
		} else {
			$this -> retError('User is not logged In');
		}
	}
	
	/**
	 *	getCurrentUser() - Returns a Users() Object instance from the $_SESSION['uid']. So, make sure this function is called after if(isset($_SESSION['uid'])) else this is going to break! No checks are made here!
	 */
	function getCurrentUser() {
		$uid = $_SESSION['uid'];
		$user = new Users();
		$us = $user -> Find('Users',array("ID"=>$uid),array());
		// The user is not checked here, cauz the user authenticated is always tend be a valid one! ;)
		return $us[0];
	}
	
	/**
	 *	getPostComments($pid,$return) - Returns all the comments of the post $pid. $return when set to true, returns Comments as an array, else display it as a JSON. Generally Supposed to be internally used. When posts are returned, so are the corresponding comments.
	 */
	private function getPostComments($pid,$return=false) {
		$post = new Posts();
		$cms = $post -> getComments($pid);
		// var_dump($cms);
		$commentsArray = array();
		
		foreach($cms as $c) {
			$cj = new CommentsJSON($c);
			$commentsArray[] = $cj;
		}
		
		switch($return) {
			case false:
				$this -> retSuccess(array('count'=>sizeof($commentsArray),'comments'=>$commentsArray));
				break;
			case true:
				return $commentsArray;
				break;
		}
	}
	
	/**
	 *	publishPost($username,$post,$return) - Publish a post. Write the post to a file at the location.
	 *	
	 *	$username - Username by whom the post is made
	 *	$contents - Post Contents (Usually in a formateed HTML content or text/plain). Note: No formatting is done at the server end. The contents are saved as plain old text, in a file generated dynamically.
	 *	$title - Title of the Post
	 *	$userRole - UserRole for which the post is currently accessible
	 *	$isPublished - Boolean value for enabling the post or saving it as Drafts
	 *	$return (Optional) - If set to true, returns the new instance of the Posts Object (created currently)
	 */
	function publishPost($username,$contents,$title,$userRole,$isPublished=true,$category=array(),$return=false) {
		/** 		
		 *			Algorithm for this function
		 *
		 *	1. Get the User
		 *	2. Set the Posts parameters
		 *	3. Connect the Post and the User
		 *	4. ForEach Category connect it to the Post, (after doing the Category checking!)
		 */
		 
		$post = new Posts();
		
		// Getting the User
		$tUsers = $post -> Find('Users',array('Username'=>$username),array());
		
		if(sizeof($tUsers) > 0) { // User Exist do the stuff
			$post -> Title = $title;
			$post -> Modified = time();
			$post -> isPublished = $isPublished;
			$post -> ViewCount = 0;
			$post -> Author = $tUsers[0] -> ID;
			
			// User Role Relation
			$user_role = $post -> Find('UserRole',array("Role"=>"Mod"),array());
			$post -> UserRole = $user_role[0] -> ID; 

			// Category Relation
			foreach($category as $tC) {
				$cat = new Category();
				$cats = $cat -> Find('Category',array('Name'=>$tC),array());
				// echo "Category Find() status: ".sizeof($cats)."<br><br>";

				if(sizeof($cats) > 0) { // Category already exist, Use the existing one
					$post -> Connect($cats[0]);
				} else {
					$cat -> Name = $tC;
					$post -> Connect($cat);
				}
			}
			// End Category Relation

			
			$fNames = array();
			$fName = 'file1';
			$post -> File = $fName; // Doing so not to make it a null value
			
			do {	// Iterate while the file name is not present! 
				$fName = md5($title.$username.time());
				$fNames = $post -> Find('Posts',array('File'=>$fName),array());
			} while(sizeof($fNames) > 0);
			
			// Alright write the post
			file_put_contents("articles".DIRECTORY_SEPARATOR.$fName.".html",$contents);
			// Test if the file is written properly. Check if its present!
			if(file_exists("articles".DIRECTORY_SEPARATOR.$fName.".html")) { // File Successfully Written!
				// Update the DB for Post object!
				// Updating the Posts! ;)
				$post -> File = $fName;
				$post -> Save(); 
				if($return) return $post;
				else $this -> retSuccess('Post Created!');
			} else {
				// There seems to be some filesystem error.
				if($return) return null;
				else $this -> retError('Post was not created successfully! There seems to be some filesystem problem');
			}
		} else {	// User doesn't exist
			if($return) return null;
			else $this -> retError('Invalid Username');
		}
		
	}
	
	/**
	 *	signUpUser($email,$userrole,$username,$password) - Signup a new User with the specified details.
	 *
	 *	$email - EMail of the User
	 *	$userRole - User Role for the new User (Can be fetched from settings as default for Any new Registered User)
	 *	$username - Username for the User. (If u want to check for the username availability, user checkUserName($username)
	 *	$password - Plain text of the User.
	 */
	function signUpUser($email,$userrole,$username,$password) {
		//TODO: Complete this function! Its not complete! Test it!
		$user = new Users();
		$users = $user -> Find('Users',array("Email = '".$email."' OR Username = '".$username."'"),array());
		if((sizeof($users) < 1)) {	// User musn't exist
			$user->Email = $email;
			$user->Username = $username;
			$user->Password = md5($password);

			// Finding a new User Role
			$user_role = $user -> Find('UserRole',array("ID"=>$userrole),array());

			$user->Connect($user_role[0]);
			$this -> retSucess($user);
		} else { // User already exist
			$this -> retError('Registration Failed!');
		}
	}
	
	/**
	 *	checkUserName($username,$return) - Checks if the given username exist or not! 
	 *
	 *	$username - Username to check for
	 *	$return (Optional) - If true, returns a boolean value, else exports the JSON data
	 */
	function checkUserName($username,$return=false) {
		$tUser = new Users();
		
		$tUsers = $tUser -> Find('Users',array('Username'=>$username),array());
		if(sizeof($tUsers) > 0) { // Username exist
			if($return) return false;
			else $this -> retError('Username not available');
		} else {	// Username available
			if($return) return true;
			else $this -> retError('Username available');
		}
	}
	
	/**
	 *	editPost($pid,$post,$isPublished,$title,$userRole) - Update the Instance of the Post with the given set of values.
	 *	
	 *	$pid - Post ID - (Mandatory)
	 *	$post (Optional)- Actual Text Containing the Post - Its only here it is written to the disk/DB. 
	 *	$isPublished (Optional) - TRUE/FALSE value stating if the post is published or not
	 *	$title (Optional) - Title of the Post - Can be null here, which'll use the existing one
	 *	$userRole (Optional) - User Role for the Post
	 */
	function editPost($pid=null,$content=null,$isPublished=null,$title=null,$userRole=null) {
		if($pid != null) {
			$post = new Posts();
			$tPosts = $post -> Find('Posts',array('ID'=>$pid),array());
			if(sizeof($tPosts) > 0) { // Post Exist do the wanted manipulation
				if($content != null)	$post -> Post = $content;	// TODO: Need to update it in a file??
				if($isPublished != null)	$post -> isPublished = $isPublished;
				if($title != null)		$post -> Title = $title;
				if($userRole != null)	$post -> UserRole = $userRole;
				$post -> Save(); // Commit the changes
				$this -> retSuccess('Successfully edited the post titled: ' + $post -> Title);
			} else {
				$this -> retError('Post ID is not Found!');
			}
		} else {
			$this -> retError('Post ID can\'t be a null value!');
		}
	}
	
	/**
	 *	addCategory($name,$return) - Add the specified Category by $name to the list!
	 *
	 *	$name - Name of the Category
	 *	$return (Optional) - Usually false, if true returns a instance of the Category Object!
	 */
	function addCategory($name,$return=false) {
		$cat = new Category();
		$cats = $cat -> Find('Category',array("Name"=>$name),array());

		if(sizeof($cats) > 0) { // Category already exist, Use the existing one
			if($return)	return $cats[0]; // There can never be more than one instance of the same named Category!
			else $this -> retError('Category '.$name.' already exist!');
		} else {
			$cat -> Name = $name;
			$cat -> Save();
			if($return) return $cat;
			else $this -> retSuccess('Category '.$name.' sucessfully created!');
		}
	}
	
	/**
	 *	addUserRole($RoleName,$Weight,$return) - Adds the specified User Role to the system.
	 *	
	 *	$RoleName - Name of the Role
	 *	$Weight - Weight of the Role - See the Documentation for more details.
	 *	$return (Optional) - If set true, returns the UserRole Object
	 */
	function addUserRole($RoleName,$Weight,$return=false) {
		$ur = new UserRole();
		$ur -> Role = $RoleName;
		$ur -> Weight = $Weight;
		$ur -> Save(); // Sends the data to the DB
		
		$jUserRole = new UserRoleJSON($ur);
		if($return) return $ur;
		else $this -> retSuccess('User Role '.$RoleName.' created!');
	}
	
	/**
	 *	editUserRole($RoleName,$weight,$return) - Edit the current User Role in the system!
	 *
	 *	$RoleName - Name of the Role to Edit
	 *	$Weight - Weight of the new Role
	 *	$return (Optional) - If set to true it returns an instance of the UserRole Object.
	 */
	function editUserRole($RoleName,$weight,$return=false) {
		$ur = new UserRole();
		$userRoles = $ur -> Find('UserRole',array('Role'=>$RoleName),array());
		if(sizeof($userRoles) > 0) {	// Object exist for Editing
			$tUserRole = $userRoles[0];
			$tUserRole -> Role = $RoleName;
			$tUserRole -> Weight = $weight;
			$tUserRole -> Save();
			
			$jUserRole = new UserRoleJSON($tUserRole);
			if($return) return $jUserRole;
			else $this -> retSuccess('User Role successfully updated!');
		} else {	// Sorry, object not found! Send the user the error back!
			if($return) return null;
			else $this -> retError('User Role '.$RoleName.' not found!');
		}
	}
	
	/**
	 *	addUserURL($url,$uid) - Add the current URL to the list for the user with $uid
	 *
	 *	$url - A new (Profile) URL to Add
	 *	$username - Valid Username of the user
	 */
	function addUserURL($url,$username) { //TODO: Test this!
		$userUrl = new UserURLs();
		$userUrl -> URL = $url;
		
		$user = new Users();
		$tUsers = $user -> Find('Users',array('Username'=>$uid),array());
		if(sizeof($tUsers) > 0) { // User Exist! Valid $uid
			$user = $tUsers[0];
			$user -> Connect($userUrl); // Sets the relation and does the saving!
		} else { // User not found! $uid not valid!
			$this -> retError('User ID is invalid');
		}
	}
	
	/**
	 *	editUserURL($oldUrl,$newUrl,$username) - Edit the previously present url of the user.
	 *
	 *	$oldUrl - Old Url present for the user.
	 *	$newUrl - New Url to replace the old one.
	 *	$username - Username for whom operation must be performed
	 */
	function editUserURL($oldUrl,$newUrl,$username) { // TODO: Test this!
		$tUserUrl = new UserURLs();
		
		$tUsers = $tUserUrl -> Find('Users',array('Username'=>$username),array());
		if(sizeof($tUsers) > 0) { // Valid User
			$tUserURLs = $tUserUrl -> Find('UserURLs',array('URL'=>$oldUrl,'User'=>$tUsers[0]->ID),array());
			if(sizeof($tUserURLs) > 0) { // Old Url exist
				$tUserUrl = $tUserURLs[0];
				$tUserUrl -> URL = $newUrl;
				$tUserUrl -> Save();
				
				$this -> retSuccess('URL '.$oldUrl.' has been changed to '.$newUrl);
			} else {
				$this -> retError('Old URL is invalid, or doesn\'t exist');
			}
		} else { // User not Found!
			$this -> retError('Invalid Username');
		}
	}
}


/**
 *	Sample implementation goes below. This is placed only for testing purpose. Final relase will not've this part of the code here. It shall be moved else where. U can see the Unit Tests 7 onwards for sample implementation!
 *
 */

//TODO: Move this code to a example file
// Example Usage Code - This shows how to use the code.

/*	UNCOMMENT THIS SECTION TO TEST DRIVE THIS APP

if(!isset($_SESSION))	session_start(); // Start the session only if its not started!

$d = new DoryJSON();

$fnc = null;

if(isset($_GET['fnc'])) {
	$fnc = $_GET['fnc'];
	switch($fnc) {	// Do the Do!
		case 'authenticateUser':
			if(isset($_GET['username']) && isset($_GET['password']))	$d -> authenticateUser($_GET['username'],$_GET['password']);
			else $d -> retError('Invalid form! Check the documentation, on how to pass the values for the function \'authenticateUser\'');
			break;
		case 'getGravatarURL':
			if(isset($_GET['email'])) $d -> getGravatarURL($_GET['email']);
			else $d -> retError('Invalid form! Check the documentation, on how to pass the values for the function \'getGravatarURL\'');
			break;
		case 'getUserPosts':
			if(isset($_GET['withComments'])) $d -> getUserPosts($_GET['withComments']);
			else $d -> getUserPosts();
			break;
		case 'getUserGravatarURL':
			$d -> getUserGravatarURL();
			break;
		case 'userLogout':
			$d -> userLogout();
			break;
		default:
			$d -> retError('Invalid Function specified!',false,true);
	}
} else {	// No function specifed! Let the developer know!
	$d -> retError('No Hacking Attempt!',true,true);
}
*/
?>
